package com.zzh.service.impl.login;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zzh.entity.*;
import com.zzh.entity.requestDto.LoginDto;
import com.zzh.entity.requestDto.SendDto;
import com.zzh.enums.RedisKey;
import com.zzh.enums.ResultEnum;
import com.zzh.mapper.*;
import com.zzh.service.login.LoginAboutService;
import com.zzh.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.core.util.Integers;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.*;
import java.util.concurrent.TimeUnit;

import com.google.gson.JsonObject;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * @author wenrouyue
 * @FileName: LoginServiceImpl
 * @description
 * @create 2023/2/11-19:15
 **/

@Slf4j
@Service
public class LoginServiceImpl implements LoginAboutService {

    @Value("${spring.mail.tittle}")
    private String tittle;
    @Value("${spring.mail.username}")
    private String fromEmail;
    @Value("${wenrouyue.maxRegisterKey}")
    private Integer maxRegisterKey;
    @Resource
    private RedisCache redisCache;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private JavaMailSender javaMailSender;
    @Resource
    private MyUtils myUtils;
    @Resource
    private UserMapper userMapper;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private UserLoginRecordMapper userLoginRecordMapper;

    @Override
    public Result<Map<String, String>> userLogin(LoginDto loginDto, HttpServletRequest request) {
        HashMap<String, String> map = new HashMap<>(10);
        map.put("msg", "用户名或密码错误!");
        try {
            String inputEmail = loginDto.getEmail();
            String password = loginDto.getPassword();
            inputEmail = SecretUtil.desEncrypt(inputEmail, SecretUtil.KEY, SecretUtil.IV);
            password = SecretUtil.desEncrypt(password, SecretUtil.KEY, SecretUtil.IV);
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(inputEmail, password);
            Authentication authenticate = authenticationManager.authenticate(authenticationToken);
            if (Objects.isNull(authenticate)) {
                return ResultUtil.getSuccess(map);
            }
            //使用userId生成token
            LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
            String userId = loginUser.getUser().getId().toString();
            String jwt = JwtUtil.createJWT(userId);
            //authenticate存入redis
            redisCache.setCacheObject("login_" + userId, loginUser, 2, TimeUnit.HOURS);
            //把token响应给前端
            HashMap<String, String> map1 = new HashMap<>(10);
            map1.put("token", jwt);
            String ipAddr = IpUtil.getIpAddr(request);
            log.info("开始记录用户登录信息,登录id:{},登录ip:{},登录账号:{}", userId, ipAddr, inputEmail);
            UserLoginRecord userLoginRecord = new UserLoginRecord(Integers.parseInt(userId), ipAddr, inputEmail);
            log.info(userLoginRecord.toString());
            //todo 用户登录记录表
//            userLoginRecordMapper.insert(userLoginRecord);
            return ResultUtil.getSuccess(map1);
        } catch (RuntimeException e) {
            log.error("login_exception:{}", e.getMessage());
            return ResultUtil.getSuccess(map);
        }
    }

    @Override
    public Result<String> send(LoginDto loginDto) {
        Integer count;
        String inputEmail = loginDto.getEmail();
        String countKey = inputEmail + "_count";
        count = redisCache.getCacheObject(countKey);
        if (Objects.isNull(count)) {
            //如果获取不到,说明是第一次发送
            if (sendCode(inputEmail)) {
                //设置第一次
                redisCache.setCacheObject(countKey, 1, 1, TimeUnit.DAYS);
                log.info("往redis中设置:{},值为:1,过期时间:1天!", countKey);
                return ResultUtil.getSuccess("验证码发送成功!");
            } else {
                throw new RuntimeException("邮箱服务异常!");
            }
        }
        int maxCount = 5;
        if (count < maxCount) {
            count = redisCache.getCacheObject(countKey);
            log.info("获取redis中用户当天发送验证码次数:{}", count);
            if (sendCode(inputEmail)) {
                redisCache.setCacheObject(countKey, count + 1, 1, TimeUnit.DAYS);
                count++;
                log.info("往redis中设置:{},值为:{},过期时间:1天!", countKey, count);
                return ResultUtil.getSuccess("验证码发送成功!");
            } else {
                throw new RuntimeException("邮箱服务异常!");
            }

        } else {
            return ResultUtil.getSuccess("每天最多只能发送5次验证码哦!");
        }
    }


    @Override
    public Result<String> register(LoginDto loginDto, HttpServletRequest request) {
        String inputEmail = loginDto.getEmail();
        String password = loginDto.getPassword();
        if (Objects.isNull(password)) {
            return ResultUtil.getError(ResultEnum.ParamException);
        }
        inputEmail = SecretUtil.desEncrypt(inputEmail, SecretUtil.KEY, SecretUtil.IV);
        password = SecretUtil.desEncrypt(password, SecretUtil.KEY, SecretUtil.IV);
        log.info("用户注册入参:{},{}", inputEmail, password);
        log.info("参数校验完成,开始注册流程,查询历史所有使用邮箱和IP");
        String redisUseRegisterKey = RedisKey.UseRegisterKey.getKey();
        String requestIp = IpUtil.getIpAddr(request);
        assert inputEmail != null;
        if (checkIsRegister(redisUseRegisterKey, inputEmail, requestIp)) {
            log.info("开始加密用户信息");
            String encodePassword = passwordEncoder.encode(password);
            User user = new User();
            BeanUtils.copyProperties(user, loginDto);
            user.setPassword(encodePassword);
            user.setEmail(inputEmail);
            user.setUsername("默认昵称" + myUtils.creatCode());
            userMapper.insert(user);
            //todo 需要绑定邮箱才可以获取3积分
            redisTemplate.opsForHash().put(redisUseRegisterKey, inputEmail, requestIp);
            log.info("往缓存中存入注册信息");
            return ResultUtil.getSuccess("注册成功!");
        }
        return ResultUtil.getError(500, "请不要重复注册,注册失败");
    }

    @Override
    public Result<String> logoutService() {
        String userId = myUtils.getLoginUserId();
        String key = "login_" + userId;
        Boolean delete = redisTemplate.delete(key);
        if (delete == null) {
            return ResultUtil.getSuccess("退出失败");
        }
        if (delete) {
            return ResultUtil.getSuccess("退出成功");
        }
        return ResultUtil.getError(ResultEnum.SystemException);
    }


    @Override
    public Result<String> sendMsg(SendDto send) {
        String userId = myUtils.getLoginUserId();
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getId, userId));
        Integer points = user.getPoints();
        synchronized (user) {
            if (points > 0) {
                log.info("积分充足,开始调用openai");
                //代理可以不设置
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890));
                String response = null;
                try {
                    if (points > 0) {
                        response = getResponse("写自己的key", send.getContent());
                        points -= 1;
                        user.setPoints(points);
                        userMapper.updateById(user);
                        log.info("调用openai成功,积分-1,剩余积分:" + points);
                        return ResultUtil.getSuccess(response);
                    }
                    return ResultUtil.getSuccess("积分不足");
                } catch (Exception e) {
                    e.printStackTrace();
                    return ResultUtil.getError(ResultEnum.SystemException);
                }
            }
        }
        return ResultUtil.getError(ResultEnum.PointException);
    }

    private ObjectMapper mapper = new ObjectMapper();
    private Map<String, Object> contextMap = new HashMap<>(); // 存储对话上下文
    private String context;

    public String getResponse(String apiKey, String message) throws Exception {
        OkHttpClient client = new OkHttpClient.Builder()
                .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890)))
                .build();
        MediaType mediaType = MediaType.parse("application/json");
        // 创建一个ObjectMapper实例
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = "";
        Map<String, Object> data = new HashMap<>();
        data.put("model", "gpt-3.5-turbo-0301");
        data.put("temperature", 0.7);
        ArrayList<HashMap<String, String>> maps = new ArrayList<>();
        HashMap<String, String> messagesMap = new HashMap<>();
        messagesMap.put("role", "user");
        messagesMap.put("content", message);
        maps.add(messagesMap);
        data.put("messages", maps);

        try {
            // 将数据转换成JSON字符串
            jsonString = objectMapper.writeValueAsString(data);
            log.info("jsonString:{}", jsonString);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        String json = String.format(
                "{\"model\":\"gpt-3.5-turbo-0301\"," +
                        "\"temperature\":0.5," +
                        "\"stop\":\"\\n\"," +
                        "\"prompt\":\"%s\"}",
                message, context);
        RequestBody body = RequestBody.create(mediaType, jsonString);

        Request request = new Request.Builder()
                .url("https://api.openai.com/v1/chat/completions")
                .post(body)
                .addHeader("Content-Type", "application/json")
                .addHeader("Authorization", "Bearer " + apiKey)
                .build();

        Response response = client.newCall(request).execute();
        String jsonResponse = response.body().string();
        log.info(jsonResponse);
        // Extract the response text from the JSON.
        int startIndex = jsonResponse.indexOf("\"text\":") + 8;
        int endIndex = jsonResponse.lastIndexOf("\"");
        String responseText = jsonResponse.substring(startIndex, endIndex);

        // Update the context with the received response.
        context += message + "\n" + responseText + "\n";

        return responseText.trim();
    }

    private boolean sendCode(String email) {
        try {
            SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
            String code = myUtils.creatCode();
            String text = "您的验证码是:" + code + ",五分钟内有效,请妥善保管,不要告诉他人哦!";
            simpleMailMessage.setFrom(fromEmail);
            simpleMailMessage.setTo(email);
            simpleMailMessage.setSubject(tittle);
            simpleMailMessage.setText(text);
            javaMailSender.send(simpleMailMessage);
            log.info("邮箱用户:{}的验证码是:{}", email, code);
            redisCache.setCacheObject(email, code, 300, TimeUnit.SECONDS);
            return true;
        } catch (MailException e) {
            e.printStackTrace();
            log.info("发送验证码异常!,{}", e.toString());
            return false;
        }
    }

    private boolean checkIsRegister(String redisKey, String key, String value) {
        Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(redisKey, ScanOptions.NONE);
        int counter = 0;
        while (cursor.hasNext()) {
            Map.Entry<Object, Object> entry = cursor.next();
            if (key.equals(entry.getKey().toString())) {
                counter++;
            }
            if (value.equals(entry.getValue().toString())) {
                counter++;
            }
        }
        return counter == 0;
    }
}
